package com.lhb.aicodemother.controller;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.lhb.aicodemother.ai.model.enums.CodeGenTypeEnum;
import com.lhb.aicodemother.annotation.AuthCheck;
import com.lhb.aicodemother.common.BaseResponse;
import com.lhb.aicodemother.common.DeleteRequest;
import com.lhb.aicodemother.common.ResultUtils;
import com.lhb.aicodemother.constant.AppConstant;
import com.lhb.aicodemother.constant.UserConstant;
import com.lhb.aicodemother.exception.BusinessException;
import com.lhb.aicodemother.exception.ErrorCode;
import com.lhb.aicodemother.exception.ThrowUtils;
import com.lhb.aicodemother.model.dto.app.*;
import com.lhb.aicodemother.model.entity.App;
import com.lhb.aicodemother.model.entity.User;
import com.lhb.aicodemother.model.vo.AppVo;
import com.lhb.aicodemother.service.AppService;
import com.lhb.aicodemother.service.ProjectDownloadService;
import com.lhb.aicodemother.service.UserService;
import com.mybatisflex.core.paginate.Page;

import com.mybatisflex.core.query.QueryWrapper;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.BeanUtils;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.File;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * 应用 控制层。
 *
 * @author lihai
 */
@RestController
@RequestMapping("/app")
public class AppController {

    @Resource
    private AppService appService;

    @Resource
    private UserService userService;

    @Resource
    private ProjectDownloadService projectDownloadService;

    /*
     * 创建应用
     * */
    @PostMapping("/add")
    public BaseResponse<Long> addApp(@RequestBody ApplicationAddRequest applicationAddRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(applicationAddRequest == null, ErrorCode.PARAMS_ERROR);
        String initPrompt = applicationAddRequest.getInitPrompt();
        ThrowUtils.throwIf(StrUtil.isBlank(initPrompt), ErrorCode.PARAMS_ERROR);
        // 获取当前登录用户
        User loginUser = userService.getLoginUser(request);
        App app = new App();
        BeanUtils.copyProperties(applicationAddRequest, app);
        app.setUserId(loginUser.getId());
        // 暂时设置为多文件
        app.setCodeGenType(CodeGenTypeEnum.VUE_PROJECT.getValue());
        app.setAppName(initPrompt.substring(0, Math.min(initPrompt.length(), 12)));
        boolean save = appService.save(app);
        ThrowUtils.throwIf(!save, ErrorCode.OPERATION_ERROR);
        return ResultUtils.success(app.getId());
    }

    /*
     * 更新应用（用户只能更新自己的应用名称）
     * */
    @PostMapping("/update")
    public BaseResponse<Boolean> updateApp(@RequestBody ApplicationUpdateRequest applicationUpdateRequest, HttpServletRequest request) {
        if (applicationUpdateRequest == null || applicationUpdateRequest.getId() == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        User loginUser = userService.getLoginUser(request);
        Long id = applicationUpdateRequest.getId();
        App byId = appService.getById(id);
        ThrowUtils.throwIf(byId == null, ErrorCode.NOT_FOUND_ERROR);
        // 仅本人可更新
        if (!loginUser.getId().equals(byId.getUserId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        App app = new App();
        app.setId(id);
        app.setAppName(applicationUpdateRequest.getAppName());
        app.setEditTime(LocalDateTime.now());
        if(StrUtil.isNotBlank(applicationUpdateRequest.getCover())){
            app.setCover(applicationUpdateRequest.getCover());
        }
        boolean b = appService.updateById(app);
        ThrowUtils.throwIf(!b, ErrorCode.OPERATION_ERROR);
        return ResultUtils.success(true);
    }

    /*
     * 删除应用（用户只能删除自己的应用）
     * */
    @PostMapping("/delete")
    public BaseResponse<Boolean> deleteApp(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) {
        if (deleteRequest == null || deleteRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        User loginUser = userService.getLoginUser(request);
        Long id = deleteRequest.getId();
        App byId = appService.getById(id);
        ThrowUtils.throwIf(byId == null, ErrorCode.NOT_FOUND_ERROR);
        if (!loginUser.getId().equals(byId.getUserId())) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
        boolean b = appService.removeById(id);
        return ResultUtils.success(b);
    }

    /*
     * 根据id获取应用详情
     * */
    @GetMapping("/get/vo")
    public BaseResponse<AppVo> getAppVoById(Long id) {
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
        // 查询数据库
        App byId = appService.getById(id);
        ThrowUtils.throwIf(byId == null, ErrorCode.NOT_FOUND_ERROR);
        AppVo appVo = appService.getAppVo(byId);
        return ResultUtils.success(appVo);
    }

    /*
     * 分页获取当前用户创建的应用列表
     * */
    @PostMapping("/my/list/page/vo")
    public BaseResponse<Page<AppVo>> listMyAppVoByPage(@RequestBody AppQueryRequest pageRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(pageRequest == null, ErrorCode.PARAMS_ERROR);
        // 限制每页的数量
        int pageNum = pageRequest.getPageNum();
        int pageSize = pageRequest.getPageSize();
        ThrowUtils.throwIf(pageSize > 20, ErrorCode.PARAMS_ERROR, "每页最多查询20个应用");
        pageRequest.setUserId(userService.getLoginUser(request).getId());
        QueryWrapper queryWrapper = appService.getQueryWrapper(pageRequest);
        Page<App> appPage = appService.page(Page.of(pageNum, pageSize), queryWrapper);
        Page<AppVo> appVoPage = new Page<>(pageNum, pageSize, appPage.getTotalRow());
        appVoPage.setRecords(appService.getAppVoList(appPage.getRecords()));
        return ResultUtils.success(appVoPage);
    }

    /*
     * 分页获取精选应用列表
     * */
    @PostMapping("/good/list/page/vo")
    public BaseResponse<Page<AppVo>> listGoodAppVoByPage(@RequestBody AppQueryRequest pageRequest) {
        ThrowUtils.throwIf(pageRequest == null, ErrorCode.PARAMS_ERROR);
        int pageNum = pageRequest.getPageNum();
        int pageSize = pageRequest.getPageSize();
        ThrowUtils.throwIf(pageSize > 20, ErrorCode.PARAMS_ERROR, "每页最多查询20个应用");
        pageRequest.setPriority(AppConstant.GOOD_APP_PRIORITY);
        Page<App> appPage = appService.page(Page.of(pageNum, pageSize), appService.getQueryWrapper(pageRequest));
        Page<AppVo> appVoPage = new Page<>(pageNum, pageSize, appPage.getTotalRow());
        appVoPage.setRecords(appService.getAppVoList(appPage.getRecords()));
        return ResultUtils.success(appVoPage);
    }

    /*
     * 管理员删除应用
     * */
    @PostMapping("/admin/delete")
    public BaseResponse<Boolean> deleteAppByAdmin(@RequestBody DeleteRequest deleteRequest) {
        if (deleteRequest == null || deleteRequest.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 查询是否存在
        Long id = deleteRequest.getId();
        App byId = appService.getById(id);
        ThrowUtils.throwIf(byId == null, ErrorCode.NOT_FOUND_ERROR);
        boolean b = appService.removeById(byId);
        return ResultUtils.success(b);
    }

    /*
     * 管理员更新应用
     * */
    @PostMapping("/admin/update")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Boolean> updateAppByAdmin(@RequestBody AppAminUpdateRequest request) {
        if (request == null || request.getId() <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long id = request.getId();
        App byId = appService.getById(id);
        ThrowUtils.throwIf(byId == null, ErrorCode.NOT_FOUND_ERROR);
        App app = new App();
        BeanUtils.copyProperties(request, app);
        app.setEditTime(LocalDateTime.now());
        boolean b = appService.updateById(app);
        ThrowUtils.throwIf(!b, ErrorCode.OPERATION_ERROR);
        return ResultUtils.success(true);
    }

    /*
     * 管理员分页获取应用列表
     * */
    @PostMapping("/admin/list/page/vo")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<Page<AppVo>> listAppVoByPageByAdmin(@RequestBody AppQueryRequest pageRequest) {
        ThrowUtils.throwIf(pageRequest == null, ErrorCode.PARAMS_ERROR);
        int pageNum = pageRequest.getPageNum();
        int pageSize = pageRequest.getPageSize();
        Page<App> appPage = appService.page(Page.of(pageNum, pageSize), appService.getQueryWrapper(pageRequest));
        Page<AppVo> appVoPage = new Page<>(pageNum, pageSize, appPage.getTotalRow());
        appVoPage.setRecords(appService.getAppVoList(appPage.getRecords()));
        return ResultUtils.success(appVoPage);
    }

    /*
     * 管理员根据id获取应用详情
     * */
    @GetMapping("/admin/get/vo")
    @AuthCheck(mustRole = UserConstant.ADMIN_ROLE)
    public BaseResponse<AppVo> getAppVoByAdmin(long id) {
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
        App byId = appService.getById(id);
        ThrowUtils.throwIf(byId == null, ErrorCode.NOT_FOUND_ERROR);
        AppVo appVo = appService.getAppVo(byId);
        return ResultUtils.success(appVo);
    }

    /*
     * 应用聊天生成代码
     * */
    @GetMapping(value = "/chat/gen/code", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<String>> chatToGenCode(@RequestParam Long appId, @RequestParam String message, HttpServletRequest request) {
        //参数校验
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR);
        ThrowUtils.throwIf(StrUtil.isBlank(message), ErrorCode.PARAMS_ERROR);

        User loginUser = userService.getLoginUser(request);
        Flux<String> stringFlux = appService.chatToGenCode(appId, message, loginUser);
        // 将内容包装为JSON返回
        return stringFlux
                .map(chunk -> {
                    Map<String, String> jsonData = Map.of("d", chunk);
                    String jsonStr = JSONUtil.toJsonStr(jsonData);
                    return ServerSentEvent.<String>builder().data(jsonStr).build();
                }).concatWith(Mono.just(
                        // 传输完毕，发送done事件给前端
                        ServerSentEvent.<String>builder()
                                .event("done")
                                .data("")
                                .build()
                ));
    }

    /*
    * 部署应用
    * */
    @PostMapping("/deploy")
    public BaseResponse<String> deployApp(@RequestBody AppDeployRequest appDeployRequest,HttpServletRequest request) {
        ThrowUtils.throwIf(appDeployRequest == null, ErrorCode.PARAMS_ERROR);
        Long appId = appDeployRequest.getAppId();
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR);
        User loginUser = userService.getLoginUser(request);
        String deployPath = appService.deployApp(appId, loginUser);
        return ResultUtils.success(deployPath);
    }

    /*
    * 下载应用代码
    * */
    @GetMapping("/download/{appId}")
    public void downloadAppCode(@PathVariable Long appId,HttpServletRequest request, HttpServletResponse response) {
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR,"应用ID无效");
        // 查询应用西悉尼
        App app = appService.getById(appId);
        ThrowUtils.throwIf(app == null, ErrorCode.NOT_FOUND_ERROR,"应用不存在");
        // 权限校验，只有应用创建者可以下载代码
        User loginUser = userService.getLoginUser(request);
        if(!app.getUserId().equals(loginUser.getId())){
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR,"无权限下载该应用代码");
        }
        // 构建应用代码目录路径
        String codeGenType = app.getCodeGenType();
        String sourceDirName = codeGenType + "_" + appId;
        String sourceDirPath = AppConstant.CODE_OUTPUT_ROOT_DIR + File.separator + sourceDirName;
        // 检查代码目录是否存在
        File fileDir = new File(sourceDirPath);
        ThrowUtils.throwIf(!fileDir.exists() || !fileDir.isDirectory(),ErrorCode.NOT_FOUND_ERROR,"应用代码不存在，请先生成代码");
        // 生成下载文件名
        String downloadFileName = String.valueOf(appId);
        // 调用通用下载服务
        projectDownloadService.downloadProjectAsZip(sourceDirPath,downloadFileName,response);
    }
}
